package com.iss.card.view.store;

import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import com.iss.card.R;
import com.iss.card.cache.BitmapConfig;
import com.iss.card.cache.ImageDataManager;
import com.iss.card.common.ApplicationManager;
import com.iss.card.common.CommonValue;
import com.iss.card.common.FunctionUtil;
import com.iss.card.common.IBackKeyListener;
import com.iss.card.common.Tools;
import com.iss.card.model.StoreTypeInfo;
import com.iss.card.view.ContainerActivity;
import com.iss.card.view.model.CityBean;
import com.iss.card.view.model.ResponseBean;
import com.iss.card.view.overrideview.IOnItemSelectedListener;
import com.iss.card.view.overrideview.ScrollViewRefresh;
import com.iss.card.view.overrideview.ScrollViewRefresh.OnRefreshListener;
import com.iss.card.webservice.BaseModel;
import com.iss.card.webservice.WebServiceAsyncService;
import com.iss.card.webservice.WebServiceAsyncService.Operation;
import com.iss.card.webservice.WebServiceAsyncService.PostExecute;
import com.iss.card.webservice.WebServiceAsyncServiceHelper;
import com.iss.card.webservice.WebServiceManager.WebServiceRegistrationParameter;
import com.iss.card.webservice.WebServiceManager.WebServiceUrlPortManager;
import com.iss.card.webservice.WebServiceUtils;

/**
 * store info.
 */
public class StoreView implements IBackKeyListener {
	private static final String TAG = StoreView.class.getSimpleName();

	public final static int CLICK_TARGET_CITY = 0x10;
	public final static int CLICK_TARGET_TYPE = 0x11;

	/** save all store data from server */
	private List<StoreModel> mMetaData = new ArrayList<StoreModel>();

	/** save store data filtered by filter rule. such as city, type, etc... */
	private List<StoreModel> mFilteredData = new ArrayList<StoreModel>();

	private Context mContext;

	/** which page is to be displayed. */
	private int mCurrentPage = 1;
	/** custom adapter for store list view container. */
	private StoreListViewAdapter mAdapter;
	/** custom viewGroup to contain all stores. */
	private StoreListViewContainer mContainer;

	private ScrollViewRefresh mScrollView;

	private TextView mCityName;
	private TextView mTypeName;

	private int mFilterPriority = EFilterPriority.EFiterCityPrior;

	private boolean mNeedCheckPromptView = true;
	/**
	 * prompt view which will be displayed while there is no data by filter
	 * rule.
	 */
	private View mPromptView;

	private View mRootView;

	/**
	 * it is used to indicate which filter rule can be changed automatically. if
	 * there is no result after filtered and the other filter can be changed
	 * automatically.
	 * 
	 * @author dtdh
	 * 
	 */
	private final class EFilterPriority {
		/** city prior, which means city can't be changed automatically. */
		public final static int EFiterCityPrior = 0x10;
		/** type prior, which means type can't be changed automatically. */
		public final static int EFiterTypePrior = 0x11;
	}

	/**
	 * it is used to indicate filtered result.
	 * 
	 * @author dtdh
	 * 
	 */
	private final class EFilteredResult {
		public final static int EFRNoResult = 0x20;
		public final static int EFRExistOnlyInCity = 0x21;
		public final static int EFRExistOnlyInType = 0x22;
		public final static int EFRExistInBoth = 0x23;
	}

	public StoreView(Context context, View view) {

		this.mContext = context;
		mRootView = view;
		initFragment(context, view);
	}

	/**
	 * set target let this know which button is clicked.
	 * 
	 * @param target
	 *            include city button and filter button.
	 */
	public void setClickTarget(int target) {
		switch (target) {
		case CLICK_TARGET_TYPE:
			mFilterPriority = EFilterPriority.EFiterTypePrior;
			break;
		case CLICK_TARGET_CITY:
			mFilterPriority = EFilterPriority.EFiterCityPrior;
			break;
		default:
			break;
		}
	}

	/**
	 * get store info.
	 */
	public void getStoreInfo(boolean isRefresh, boolean isShowProgressBar) {
		requestGetStoreList();
	}

	/**
	 * redraw current view in window.
	 */
	public void invalidate() {
		if (mRootView != null) {
			mRootView.invalidate();
		}
	}

	public void onHiddenChanged(boolean hidden) {
		if (!hidden) {
			needShowPromptView();
		}
	}

	/**
	 * init ui.
	 * 
	 * @param context
	 * @param view
	 */
	private void initFragment(Context context, View view) {
		mPromptView = view.findViewById(R.id.view_empty_prompt);
		((Button) mPromptView.findViewById(R.id.bt_empty_prompt_ok))
				.setOnClickListener(new OnClickListener() {
					@Override
					public void onClick(View v) {
						Log.d(TAG, "onDismissPromptView");
						if (mPromptView != null) {
							mPromptView.setVisibility(View.GONE);
						}

						updateDataByFilterRule();
					}
				});

		mCityName = (TextView) view.findViewById(R.id.store_city_name);
		mTypeName = (TextView) view.findViewById(R.id.store_type_name);
		mScrollView = (ScrollViewRefresh) view.findViewById(R.id.scrollview);
		mScrollView.getViewTreeObserver().addOnPreDrawListener(
				new OnPreDrawListener() {

					@Override
					public boolean onPreDraw() {
						mContainer.setHeight(mScrollView.getMeasuredHeight());
						mScrollView.getViewTreeObserver()
								.removeOnPreDrawListener(this);
						return true;
					}

				});

		mAdapter = new StoreListViewAdapter(context);
		mContainer = new StoreListViewContainer(context);
		mContainer.setOnItemSelectedListener(mOnItemSelectedListener);
		mContainer.setAdapter(mAdapter);
		mScrollView.addChild(mContainer, 1);
		mScrollView.setOnScrollChangedListener(mContainer);
//		mScrollView.setFooterListener(footerlistener);
		mScrollView.setVisibility(View.INVISIBLE);
		mScrollView.setonRefreshListener(new OnRefreshListener() {

			public void onRefresh() {
				Log.d(TAG, "onRefresh");
				if (mScrollView.state == ScrollViewRefresh.REFRESHING) {
					mCurrentPage = 1;
					getStoreInfo(true, false);
				}
			}
		});

		requestGetStoreList();
	}

	/**
	 * click listener for footer view.
	 */
	OnClickListener footerlistener = new OnClickListener() {

		@Override
		public void onClick(View v) {
			// show another pic show.
			Log.i(TAG, "onClick:footerlistener");
			mCurrentPage++;
			mAdapter.setContents(fetchDisplayedStoreList());
			if (mAdapter.getCount() == mFilteredData.size()) {
				mScrollView.setFooterViewText(R.string.loading_end);
			}
		}
	};

	/**
	 * it is used to listener item selected event. it will be invoked while one
	 * store in list is clicked.
	 */
	private IOnItemSelectedListener mOnItemSelectedListener = new IOnItemSelectedListener() {

		@Override
		public void OnItemSelected(ViewGroup parent, View view, int position,
				Object item) {
			Intent intent = new Intent(mContext, ContainerActivity.class);
			intent.putExtra(ApplicationManager.KEY_PUT_EXTRA_SELECTED_STORE_ID,
					((StoreModel) item).getStoreID());
			// jump to store detail page and let the first product as
			// default one.
			intent.putExtra(
					ApplicationManager.KEY_PUT_EXTRA_DEFAULT_PRODUCT_ID, 0);
			intent.putExtra(ApplicationManager.INTENT_KEY,
					ApplicationManager.INTENT_FRAGMENT_STORE_DETAIL);
			((Activity) mContext).startActivityForResult(intent, 0);
		}
	};

	/**
	 * fetch sub list from filtered data. because there are
	 * CommonValue.COUNT_SHOW stores should be displayed in one page.
	 * 
	 * @return to be displayed data.
	 */
	private List<StoreModel> fetchDisplayedStoreList() {
//		final int curNum = mCurrentPage * CommonValue.COUNT_SHOW;
//		final int totalNum = mFilteredData.size();
//		int maxNum = totalNum > curNum ? curNum : totalNum;
//		List<StoreModel> list = mFilteredData.subList(0, maxNum);
//		return list;
		
		return mFilteredData;
	}

	/**
	 * request store list from web server.
	 */
	private void requestGetStoreList() {
		Log.i(TAG, "requestGetStoreList: ");
		WebServiceAsyncService service = new WebServiceAsyncService();
		service.setParameterToAsyncService(mContext, service.getNextToken(),
				Operation.OP_SERVICE_ARG_SHOPS_INFO,
				WebServiceRegistrationParameter.SHOPS_INFO_PARAMETER,
				new String[] {},
				WebServiceUrlPortManager.ACTION_URL_PORT_SHOPS_INFO, this,
				WebServiceAsyncServiceHelper.TYPE_HTTP_GET_INSTREAM);
		service.setPostExecute(new PostExecute() {
			@Override
			public void onAsyncServiceComplete(int token, Object result,
					int index) {
				mScrollView.onRefreshComplete();
				responseGetStoreList(result);
			}
		});
	}

	/**
	 * response for getStoreList request.
	 * 
	 * @param result
	 *            response information.
	 */
	private void responseGetStoreList(Object result) {
		Log.d(TAG, "responseGetStoreList");
		ResponseBean response = WebServiceUtils.getObjectResultToBaseInfo(
				result, StoreModel.class);
		if (Tools.onPostCheck(mContext, response)) {
			return;
		}

		List<? extends BaseModel> list = response.getReturnList();
		mMetaData.clear();
		for (BaseModel item : list) {
			mMetaData.add((StoreModel) item);
			Log.d(TAG,
					"responseGetStoreList: sid="
							+ ((StoreModel) item).getStoreID() + ", picUrl="
							+ ((StoreModel) item).getPicUrl()
							+ ", storeAddress="
							+ ((StoreModel) item).getStoreAdress() + ", type="
							+ ((StoreModel) item).getStoreType() + ", city="
							+ ((StoreModel) item).getStoreCityName()
							+ ", cityId="
							+ ((StoreModel) item).getStoreCityID());
		}

		// if it is the first time, check is there store list or not. if there
		// is no one according to filter rule, then show prompt view. otherwise
		// don't show it.
		if (mNeedCheckPromptView) {
			mNeedCheckPromptView = false;
			needShowPromptView();
		} else {
			updateDataByFilterRule();
		}
	}

	/**
	 * show prompt view with correct background.
	 */
	private void needShowPromptView() {
		CityBean city = FunctionUtil.getSelectedCityInfo();
		StoreTypeInfo storeType = FunctionUtil.getSelectedStoreType();
		final int result = checkFilteredResult(city, storeType);
		Log.d(TAG, "needShowPromptView: result=" + result);
		switch (result) {
		case EFilteredResult.EFRExistInBoth:
			mPromptView.setVisibility(View.GONE);
			mScrollView.setVisibility(View.VISIBLE);
			updateDataByFilterRule();
			break;
		case EFilteredResult.EFRExistOnlyInCity:
			mPromptView.setVisibility(View.VISIBLE);
			mScrollView.setVisibility(View.GONE);
			setEmptyPromptViewBackground(R.drawable.prompt_no_store_in_type);
			mCityName.setText(city.getCityName());
			mTypeName.setText(storeType.getTypeName());
			break;
		case EFilteredResult.EFRExistOnlyInType:
			mPromptView.setVisibility(View.VISIBLE);
			mScrollView.setVisibility(View.GONE);
			setEmptyPromptViewBackground(R.drawable.prompt_no_store_in_city);
			mCityName.setText(city.getCityName());
			mTypeName.setText(storeType.getTypeName());
			break;
		case EFilteredResult.EFRNoResult:
			mPromptView.setVisibility(View.VISIBLE);
			mScrollView.setVisibility(View.GONE);
			setEmptyPromptViewBackground(R.drawable.prompt_no_store_in_city_and_type);
			mCityName.setText(city.getCityName());
			mTypeName.setText(storeType.getTypeName());
			break;
		default:
			break;
		}
	}

	private void setEmptyPromptViewBackground(int resId) {
		if (mPromptView == null) {
			return;
		}
		((ImageView) mPromptView
				.findViewById(R.id.iv_store_list_view_empty_prompt))
				.setImageResource(resId);
	}

	/**
	 * check which case should be used according to city rule and type rule.
	 * case 1. there are stores in selected city, and most of them are in
	 * selected type. then EFRExistInBoth should be return. case 2. there are
	 * stores in selected city, and none of them are in selected type. then
	 * EFRExistOnlyInCity should be return. case 3. there are stores in selected
	 * type, and none of them are in selected city. then EFRExistOnlyInType
	 * should be return. case 4. there is no store in selected type and selected
	 * city. then EFRNoResult should be return.
	 * 
	 * @param city
	 *            current selected city information.
	 * @param type
	 *            current selected store type information.
	 * @return which case is used after filter rule applied.
	 */
	private int checkFilteredResult(CityBean city, StoreTypeInfo type) {
		int result = EFilteredResult.EFRNoResult;
		List<StoreModel> tmp = new ArrayList<StoreModel>();
		switch (mFilterPriority) {
		case EFilterPriority.EFiterCityPrior:
			if ("0".equals(city.getStoreNumber())) { // filter by city.
				result = EFilteredResult.EFRNoResult;
			} else {
				for (StoreModel store : mMetaData) {
					if (store.getStoreCityID().equals(city.getCityId())) {
						tmp.add(store);
					}
				}
				if (tmp.size() > 0) {
					result = EFilteredResult.EFRExistOnlyInCity;
				}

				// filter by type.
				if (type.getTypeId().equals(CommonValue.STORE_TYPE_ID_ALL)) {
					result = (result == EFilteredResult.EFRExistOnlyInCity) ? EFilteredResult.EFRExistInBoth
							: result;
				} else {
					for (StoreModel store : tmp) {
						if (store.getStoreType().equals(type.getTypeId())) {
							result = EFilteredResult.EFRExistInBoth;
							break;
						}
					}
				}
			}
			break;
		case EFilterPriority.EFiterTypePrior:
			if (type.getTypeId().equals(CommonValue.STORE_TYPE_ID_ALL)) {
				for (StoreModel store : mMetaData) {
					tmp.add(store);
				}
			} else {
				for (StoreModel store : mMetaData) {
					if (store.getStoreType().equals(type.getTypeId())) {
						tmp.add(store);
					}
				}
			}

			if (tmp.size() > 0) {
				result = EFilteredResult.EFRExistOnlyInType;
			}

			if ("0".equals(city.getStoreNumber())) {

			} else {
				for (StoreModel store : tmp) {
					if (store.getStoreCityID().equals(city.getCityId())) {
						result = (result == EFilteredResult.EFRExistOnlyInType) ? EFilteredResult.EFRExistInBoth
								: EFilteredResult.EFRExistOnlyInCity;
						break;
					}
				}
			}

			break;
		default:
			break;
		}
		return result;
	}

	private void resetPositionInformation() {
		for (StoreModel store : mMetaData) {
			store.left = 0;
			store.right = 0;
			store.bottom = 0;
			store.top = 0;
		}
	}

	/**
	 * update valid data according to filter rule.
	 */
	private void updateDataByFilterRule() {
		mFilteredData.clear();
		CityBean city = FunctionUtil.getSelectedCityInfo();
		StoreTypeInfo storeType = FunctionUtil.getSelectedStoreType();
		List<StoreModel> tmp = new ArrayList<StoreModel>();
		String cityName = "";
		String typeName = "";
		resetPositionInformation();
		Resources r = mContext.getResources();
		switch (mFilterPriority) {
		case EFilterPriority.EFiterCityPrior:
			if ("0".equals(city.getStoreNumber())) { // filter by city.
				for (StoreModel store : mMetaData) {
					tmp.add(store);
				}
				cityName = r.getText(R.string.store_list_view_city_all)
						.toString();
			} else {
				for (StoreModel store : mMetaData) {
					if (store.getStoreCityID().equals(city.getCityId())) {
						tmp.add(store);
					}
				}

				if (tmp.size() == 0) {
					tmp = mMetaData;
					cityName = r.getText(R.string.store_list_view_city_all)
							.toString();
				} else {
					cityName = city.getCityName();
				}
			}

			typeName = storeType.getTypeName();
			if (storeType.getTypeId().equals(CommonValue.STORE_TYPE_ID_ALL)) { // filter
																				// by
																				// type.
				for (StoreModel store : tmp) {
					mFilteredData.add(store);
				}
			} else {
				for (StoreModel store : tmp) {
					if (store.getStoreType().equals(storeType.getTypeId())) {
						mFilteredData.add(store);
					}
				}
			}

			if (mFilteredData.size() == 0) {
				mFilteredData = tmp;
				typeName = r.getText(R.string.store_list_view_type_all)
						.toString();
				storeType.setTypeId(CommonValue.STORE_TYPE_ID_ALL);
				storeType.setTypeName(typeName);
				FunctionUtil.setSelectedStoreTypeInfo(storeType);
			}

			break;
		case EFilterPriority.EFiterTypePrior:
			typeName = storeType.getTypeName();
			if (storeType.getTypeId().equals(CommonValue.STORE_TYPE_ID_ALL)) {
				for (StoreModel store : mMetaData) {
					tmp.add(store);
				}
			} else {
				for (StoreModel store : mMetaData) {
					if (store.getStoreType().equals(storeType.getTypeId())) {
						tmp.add(store);
					}
				}

				if (tmp.size() == 0) {
					tmp = mMetaData;
					typeName = r.getText(R.string.store_list_view_type_all)
							.toString();
					storeType.setTypeId(CommonValue.STORE_TYPE_ID_ALL);
					storeType.setTypeName(typeName);
					FunctionUtil.setSelectedStoreTypeInfo(storeType);
				}
			}

			if ("0".equals(city.getStoreNumber())) {
				for (StoreModel store : tmp) {
					mFilteredData.add(store);
				}
				cityName = r.getText(R.string.store_list_view_city_all)
						.toString();
			} else {
				for (StoreModel store : tmp) {
					if (store.getStoreCityID().equals(city.getCityId())) {
						mFilteredData.add(store);
					}
				}
				cityName = city.getCityName();
			}
			if (mFilteredData.size() == 0) {
				mFilteredData = tmp;
				cityName = r.getText(R.string.store_list_view_city_all)
						.toString();
			}
			break;
		default:
			break;
		}

		mCityName.setText(cityName);
		mTypeName.setText(typeName);
		refreshStoreListView();
	}

	private void refreshStoreListView() {
		Log.d(TAG, "refreshStoreListView: num=" + mFilteredData.size());
		mScrollView.setVisibility(View.VISIBLE);
		mAdapter.setContents(fetchDisplayedStoreList());
		mScrollView.setFooterVisible(true);
		mScrollView.setFooterViewText(R.string.loading_end);
//		if (mFilteredData.size() > CommonValue.COUNT_SHOW) {
//			mScrollView.setFooterViewText(R.string.loading_more);
//		} else {
//			mScrollView.setFooterViewText(R.string.loading_end);
//		}
	}

	@Override
	public void cancelUpdate(Object... params) {
		if (mAdapter == null || mAdapter.getCount() == 0) {
		} else {
			mScrollView.onRefreshComplete();
		}
	}

	private final class StoreListViewAdapter extends BaseAdapter {
		private List<StoreModel> mContents = new ArrayList<StoreModel>();
		private Context mContext = null;

		public StoreListViewAdapter(Context ct) {
			this.mContext = ct;
		}

		public void setContents(List<StoreModel> data) {
			mContents.clear();
			mContents.addAll(data);
			mScrollView.scrollTo(0, 0);
			Log.d(TAG, "setContents: size =" + mContents.size());
			this.notifyDataSetChanged();
		}

		@Override
		public int getCount() {
			return mContents.size();
		}

		@Override
		public Object getItem(int position) {
			return mContents.get(position);
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {
			HoldView hv = null;
			// Log.d(TAG, "getView: view= " + convertView + ", pos=" +
			// position);
			if (convertView == null) {
				hv = new HoldView();
				convertView = LayoutInflater.from(this.mContext).inflate(
						R.layout.store_list_store_item, null);
				hv.iv = (ImageView) convertView
						.findViewById(R.id.iv_store_list_store_item);
				convertView.setTag(hv);
			} else {
				hv = (HoldView) convertView.getTag();
			}

			StoreModel info = mContents.get(position);
			String url = info.getPicUrl();
			Log.i(TAG, "getView: position =" + position + ";url = " + url
					+ ", " + hv.iv);
			hv.iv.setTag(url);
//			final int w = ApplicationManager.SCREEN_WIDTH;
//			final int picW = Integer.valueOf(info.getPicWidth());
//			final int picH = Integer.valueOf(info.getPicHeight());
//			ImageDataManager.instance().loadBitmap(url, hv.iv,
//					new BitmapConfig(w, (picH * w / picW)));

			return convertView;
		}

		private final class HoldView {
			ImageView iv;
		}
	}
}
